Skip to main content

Shallow Copy vs Deep Copy : Difference with Practical Examples

 

When working with objects in programming, particularly in C#, the concepts of shallow copy and deep copy come into play. These two terms can cause confusion, especially when dealing with mutable data, but they are essential for understanding how object references and memory work in .NET.

Let’s dive into the difference between shallow copy and deep copy, their use cases, and why it’s important to understand them.

What is a Shallow Copy?

A shallow copy duplicates an object, but only at the first level. The new object is created, and its fields are copied over, but if those fields reference other objects, only the references are copied—not the actual objects themselves.

In other words, a shallow copy creates a new instance of the top-level object, but the nested objects inside it will still point to the same memory location as the original object. This can lead to unexpected behaviors if you modify the nested objects in one instance.

Example: Shallow Copy in Action

Let’s say we have a Person class with a Name and an Address object. Here’s what happens with a shallow copy:

public class Address {
    public string Street { get; set; }
}

public class Person {
    public string Name { get; set; }
    public Address Address { get; set; }

    public Person ShallowCopy() {
        return (Person)this.MemberwiseClone();
    }
}

Person person1 = new Person {
    Name = "John",
    Address = new Address { Street = "123 Main St" }
};

// Perform a shallow copy
Person person2 = person1.ShallowCopy();

// Modify the address of the second person
person2.Address.Street = "456 Park Ave";

Console.WriteLine(person1.Address.Street);  // Output: "456 Park Ave"

In this example:

  • We copied person1 into person2 using the ShallowCopy method.
  • Changing the Street property of person2 also changed person1’s Street because both person1 and person2 share the same Address reference.

What is a Deep Copy?

A deep copy, on the other hand, duplicates not just the object itself, but all the objects it references. This means that when you create a deep copy, new instances are created for both the main object and any objects it contains, effectively breaking the reference chain.

With a deep copy, changes to the copied object won’t affect the original object, even if those changes involve modifying nested objects.

Example: Deep Copy in Action

Now, let’s create a method for deep copying the Person class:

public class Address {
    public string Street { get; set; }

    public Address DeepCopy() {
        return new Address { Street = this.Street };
    }
}

public class Person {
    public string Name { get; set; }
    public Address Address { get; set; }

    public Person DeepCopy() {
        return new Person {
            Name = this.Name,
            Address = this.Address.DeepCopy()
        };
    }
}

Person person1 = new Person {
    Name = "John",
    Address = new Address { Street = "123 Main St" }
};

// Perform a deep copy
Person person2 = person1.DeepCopy();

// Modify the address of the second person
person2.Address.Street = "456 Park Ave";

Console.WriteLine(person1.Address.Street);  // Output: "123 Main St"

In this example:

  • We created a deep copy of person1 into person2.
  • Since person2 has its own Address object, modifying the Street for person2 doesn’t affect person1.

When Should You Use Shallow Copy vs Deep Copy?

Understanding when to use shallow copy versus deep copy depends on the structure of your data and the behavior you want to achieve.

  • Use Shallow Copy if:

    • The objects you are working with don’t contain references to other mutable objects.
    • You are okay with having multiple references to the same objects.
  • Use Deep Copy if:

    • Your object contains references to other objects (especially if they are mutable), and you want to ensure the copy is completely independent of the original.
    • You want to avoid side effects caused by changes to shared references.

Understanding the Performance Implications

One thing to keep in mind is that deep copying can be expensive in terms of performance, especially if you have a complex object graph with many nested objects. Each referenced object needs to be copied, which can take time and resources.

On the other hand, shallow copying is typically faster since it only copies the top-level structure and references.

Deep Copy Performance Consideration

If you’re dealing with large collections or deeply nested objects, it’s important to weigh the cost of deep copying versus the risk of side effects from shallow copying. Often, you might not need a deep copy unless you explicitly require full independence between instances.

Using Libraries for Deep Copying

In some cases, manually implementing deep copy logic for every class can be tedious and error-prone, especially if your classes have a lot of fields or nested objects.

You can use libraries like AutoMapper or Newtonsoft.Json to simplify deep copying:

Deep Copy with Newtonsoft.Json

Person person2 = JsonConvert.DeserializeObject<Person>(JsonConvert.SerializeObject(person1));

This uses serialization and deserialization to create a deep copy. It works well for simple objects but may not handle certain complex types (like delegates or events).

Conclusion: Shallow Copy vs Deep Copy

To summarize, shallow copy and deep copy are two important concepts in object-oriented programming, especially when dealing with complex objects that contain references. While shallow copying is faster and simpler, it may lead to unintended side effects if the copied object references mutable objects. Deep copying, on the other hand, ensures that you get a completely independent copy of the object, but at the cost of performance.

Understanding when and how to use each method is crucial for writing clean, maintainable, and bug-free code. Whether you choose shallow or deep copying, make sure it aligns with the behavior and performance needs of your application.

Do you have experience using shallow or deep copying in your projects? Share your thoughts in the comments below—let’s get the conversation started!

Comments

Popular posts from this blog

Implementing and Integrating RabbitMQ in .NET Core Application: Shopping Cart and Order API

RabbitMQ is a robust message broker that enables communication between services in a decoupled, reliable manner. In this guide, we’ll implement RabbitMQ in a .NET Core application to connect two microservices: Shopping Cart API (Producer) and Order API (Consumer). 1. Prerequisites Install RabbitMQ locally or on a server. Default Management UI: http://localhost:15672 Default Credentials: guest/guest Install the RabbitMQ.Client package for .NET: dotnet add package RabbitMQ.Client 2. Architecture Overview Shopping Cart API (Producer): Sends a message when a user places an order. RabbitMQ : Acts as the broker to hold the message. Order API (Consumer): Receives the message and processes the order. 3. RabbitMQ Producer: Shopping Cart API Step 1: Install RabbitMQ.Client Ensure the RabbitMQ client library is installed: dotnet add package RabbitMQ.Client Step 2: Create the Producer Service Add a RabbitMQProducer class to send messages. RabbitMQProducer.cs : using RabbitMQ.Client; usin...

How Does My .NET Core Application Build Once and Run Everywhere?

One of the most powerful features of .NET Core is its cross-platform nature. Unlike the traditional .NET Framework, which was limited to Windows, .NET Core allows you to build your application once and run it on Windows , Linux , or macOS . This makes it an excellent choice for modern, scalable, and portable applications. In this blog, we’ll explore how .NET Core achieves this, the underlying architecture, and how you can leverage it to make your applications truly cross-platform. Key Features of .NET Core for Cross-Platform Development Platform Independence : .NET Core Runtime is available for multiple platforms (Windows, Linux, macOS). Applications can run seamlessly without platform-specific adjustments. Build Once, Run Anywhere : Compile your code once and deploy it on any OS with minimal effort. Self-Contained Deployment : .NET Core apps can include the runtime in the deployment package, making them independent of the host system's installed runtime. Standardized Libraries ...

Clean Architecture: What It Is and How It Differs from Microservices

In the tech world, buzzwords like   Clean Architecture   and   Microservices   often dominate discussions about building scalable, maintainable applications. But what exactly is Clean Architecture? How does it compare to Microservices? And most importantly, is it more efficient? Let’s break it all down, from understanding the core principles of Clean Architecture to comparing it with Microservices. By the end of this blog, you’ll know when to use each and why Clean Architecture might just be the silent hero your projects need. What is Clean Architecture? Clean Architecture  is a design paradigm introduced by Robert C. Martin (Uncle Bob) in his book  Clean Architecture: A Craftsman’s Guide to Software Structure and Design . It’s an evolution of layered architecture, focusing on organizing code in a way that makes it  flexible ,  testable , and  easy to maintain . Core Principles of Clean Architecture Dependency Inversion : High-level modules s...